import { Node, Prefab, SpriteFrame } from "cc";
import Singleton from "../Base/Singleton";
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from "../Common";
import { ActorMgr } from "../Entity/Actor/ActorMgr";
import { Joystick } from "../UI/Joystick";
import { BulletMgr } from "../Entity/Bullet/BulletMgr";
import EventManager from "./EventManager";
import { EventEnum } from "../Enum";
import { randomBySeed } from "../Utils";

const ACTOR_SPEED = 100;
const BULLET_SPEED = 600;

const MAP_HIGHT = 640;
const MAP_WIDTH = 960;

const ACTOR_RADIUS = 50;
const BULLET_RADIUS = 10;
const BULLET_DAMAGE = 10;

export default class DataManager extends Singleton {
  static get Instance() {
    return super.GetInstance<DataManager>();
  }

  myPlayerId: number = 1;
  frameId: number = 0;
  roomInfo: IRoom;
  jm: Joystick;

  stage: Node;
  actorMap: Map<number, ActorMgr> = new Map();
  bulletMap: Map<number, BulletMgr> = new Map();
  prefabMap: Map<string, Prefab> = new Map();
  textureMap: Map<string, SpriteFrame[]> = new Map();

  State: IState = {
    actors: [],
    bullets: [],
    nextBulletId: 1,
    seed: 1,
  }

  lastState: IState;

  applyInput(input: IClientInput) {
    switch (input.type) {

      case InputTypeEnum.ActorMove: {//玩家移动
        const { id, type, direction, dt } = input;
        const actor = this.State.actors.find((actor) => actor.id === id);

        actor.direction.x = direction.x
        actor.direction.y = direction.y

        actor.position.x += toFixed(direction.x * ACTOR_SPEED * dt);
        actor.position.y += toFixed(direction.y * ACTOR_SPEED * dt);
        break;
      }

      case InputTypeEnum.WeaponShoot: {//开枪
        const { owner, position, direction } = input;
        const bullet: IBullet = {
          id: this.State.nextBulletId++,
          type: this.actorMap.get(owner).bulletType,
          owner,
          position,
          direction,
        }

        EventManager.Instance.emit(EventEnum.BulletBorn, owner);
        this.State.bullets.push(bullet);
        break;
      }

      case InputTypeEnum.TimePast: {//时间流逝
        const { dt } = input;
        const { bullets, actors } = this.State;

        for (let i = bullets.length - 1; i >= 0; i--) {
          const bullet = bullets[i];

          for (let j = actors.length - 1; j >= 0; j--) {
            const actor = actors[j];

            if ((actor.position.x - bullet.position.x) ** 2 +
              (actor.position.y - bullet.position.y) ** 2 <
              (BULLET_RADIUS + ACTOR_RADIUS) ** 2
            ) {
              const random = randomBySeed(this.State.seed);
              this.State.seed = random;
              const damage = random / 233280 >= 0.5 ? BULLET_DAMAGE * 2 : BULLET_DAMAGE;
              actor.hp -= damage;
              EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
                x: toFixed((actor.position.x + bullet.position.x) / 2),
                y: toFixed((actor.position.y + bullet.position.y) / 2)
              });
              bullets.splice(i, 1);
              break;
            }
          }

          if (Math.abs(bullet.position.x) > MAP_WIDTH / 2 || Math.abs(bullet.position.y) > MAP_HIGHT / 2) {
            EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, { x: bullet.position.x, y: bullet.position.y });
            bullets.splice(i, 1);
            break;
          }
        }

        for (const bullet of bullets) {
          bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt);
          bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt);
        }
      }

      default:
        break;
    }
  }

}
